/* MainController.m
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by Mai Nguyen, NeXT Developer Support
 *
 */

#import "MainController.h"
#import "Department.h"
#import "strings.h"

@implementation MainController

- init
{
	[super init];
	return self;
}

- appDidInit:sender
{
	
	dbDataSource = [masterController dataSource];
	dbChannel = [dbDataSource databaseChannel];
	adaptorChannel = [dbChannel adaptorChannel];
	eoAdaptor = [[adaptorChannel adaptorContext] adaptor];
	
	
	// Bring up a login panel and get the valid connection dictionary
	
 	if(![eoAdaptor runLoginPanelAndValidateConnectionDictionary]) 
		[NXApp terminate:self];
    [eoAdaptor setConnectionDictionary: [eoAdaptor connectionDictionary]];

	rootEntity = [dbDataSource entity];
	
	[self setDelegates];
	
	// set up the controller to not alter the selection after a fetch  
	[masterController setSelectsFirstObjectAfterFetch:NO];

	[self fetch:sender];	
	return self;
}

// Perform a fetch based on the latest options specified by user.  

- fetch:sender
{
	if (optionsPanel)
		[optionsPanel orderOut:sender];
		
	[self setUpFetch:sender];

	[masterController fetch];
	return self;
}

- newRecord:sender
{
	int i;
	
	// Clear all the current values in the form cells to prepare
	// for the insertion of a new record.
	 
	 for (i = 0; i < 5; i++)
		[formMatrix setStringValue:"" at:i];
	return self;
}

- insert:sender
{
	// Disable the INSERT Button if the input is not valid  
	if ([self validateRecord] == NO) {
		[insertButton setState:0];
		NXRunAlertPanel(NULL, ERR_INSERT_FAIL, NULL, NULL, NULL);
	}
	else {
		[insertButton setState:1];
		[masterController insert:sender];
	}
	return self;
}
 

// Set up the fetch order and specify a qualifier 
// for the master table "Department" 
 
- setUpFetch:sender
{ 
	const char	*inputString;
	const char	*attrName = NULL;
	EOQualifier *aQualifier;
	id			 fetchOrder;
	id			 sortAttribute;
	int			 orderType;

	// build the qualifier.
	// If the input string is empty, fetch all records.
	 
	inputString = [(TextField *)textField stringValue];
	 
	if (inputString == NULL)
		[dbDataSource setQualifier: [rootEntity qualifier]];
	else {
	 	aQualifier = [[[EOQualifier alloc] initWithEntity:rootEntity
						qualifierFormat:@"%A >= %d", @"DeptId", 
						[textField intValue]] autorelease];
		[dbDataSource setQualifier:aQualifier];
		 }
													 
	// build the fetch order based on the first column of the master
	// tableview
	  
	orderType = [sortMatrix selectedTag];
	attrName  = [[masterTableview columnAt:0] title];
	sortAttribute = [rootEntity attributeNamed:[ [[NSString alloc]
								initWithCString:attrName] autorelease]];
	fetchOrder = [NSArray arrayWithObject:[[[EOAttributeOrdering alloc]
    	 initWithAttribute:sortAttribute ordering: orderType] autorelease]];
	 		
	[dbDataSource setFetchOrder:fetchOrder];
   	return self;
}


- (BOOL) validateRecord
{
	int newId, newLocation;
	BOOL result = YES;
	
	newId = [formMatrix intValueAt:0];
	newLocation = [formMatrix intValueAt:2];
	
	// Do validation here  
	if ( newId < 100 || newId > 999) {
		NXRunAlertPanel(NULL, ERR_INVALID_ID, NULL, NULL, NULL);
		result = NO;
	}
	if ( newLocation != 1101 && newLocation != 1103
			&& newLocation != 1104 && newLocation != 1106
			&& newLocation != 1207) {
		NXRunAlertPanel(NULL, ERR_INVALID_LOCATION, NULL, NULL, NULL);
		result = NO;
	} 
	return result;
	 
}
- setDelegates
{
#ifdef DEBUG 	
	[adaptorChannel setDebugEnabled:YES];
#endif 
	[adaptorChannel setDelegate:self];
	[(EOController *) masterController setDelegate:self];
	
	return self;
}

@end

@implementation MainController (EOAdaptorDelegation)

//This method is useful to trace SQL queries  
- (void)adaptorChannel:channel
    didEvaluateExpression:(NSString *)expression
{
	if (sqlPanel) {
		[text appendToText:"SQL Query:\n"];
		[text appendToText:[expression cString]];
		[text appendToText:"\n"];
	}
}
@end
 

@implementation MainController (EOControllerDelegation)

// This method is called before each update. Since the controller has
// buffer edits turned ON, this method is called when the user explicitly
// presses the UPDATE button. 
 
- (NSDictionary *)controller:(EOController *)controller 
	willSaveEdits: (NSDictionary *)edits 
	toObject:object
{
	if ([self validateRecord] == YES)
			return edits;
	else {
		NXRunAlertPanel(NULL, ERR_UPDATE_FAIL, NULL, NULL, NULL);
		return nil;
	}
}

// Take the input from the formcells and create a new record.
// This delegate method can be used to set up the default values of a newly
// created EO. By default, we use the current user input in the formcells
// to create a new record.
 
 
- (BOOL)controller:(EOController *)controller willInsertObject:object atIndex: (unsigned)newIndex
{
	[object setDeptId: (int)[formMatrix intValueAt:0]];
	[object setDepartmentName:[[[NSString alloc] initWithCString: 
					[formMatrix stringValueAt:1]] autorelease]];
	[object setLocationId:(int)[formMatrix intValueAt:2]];
		 // The to-many relationship points to an empty autoreleased array    
	[object setToEmployee:[NSArray array]];
	return YES;
	
}
 
// After insertion, refetch object so that the derived attributes are 
// properly redisplayed.
 
- (void)controller:(EOController *)controller didInsertObject:object
    inDataSource:dataSource
{
	[dbChannel refetchObject:object];
}

// After an update, refetch object so that the derived attributes are 
// properly redisplayed.
 
- (void)controller:(EOController *)controller didUpdateObject:object
    inDataSource:dataSource
{
	[dbChannel refetchObject:object];
}	

// When the insert operation failed, remove the wrong record and rollback
// the data source. A failure usually happens when trying to insert a duplicate
// key. Since an insert operation adds a new record after the current
// selection, the index has to be computed accordingly.
 
 
- (EODataSourceFailureResponse)controller:(EOController *)controller
    failedToInsertObject:object
    inDataSource:dataSource;
{
	NXRunAlertPanel(NULL, ERR_INSERT_FAIL, NULL, NULL, NULL);
	 
	[controller deleteObjectAtIndex:([masterTableview selectedRow] + 1)];
	
	return EORollbackDataSourceFailureResponse;
}

- (EODataSourceFailureResponse) controller:(EOController *)controller
    failedToUpdateObject:object
    inDataSource:dataSource
{
	NXRunAlertPanel(NULL, ERR_UPDATE_FAIL, NULL, NULL, NULL);
		// Restore object to its previous state
	[dbChannel refetchObject:object];
	
	return EORollbackDataSourceFailureResponse;
}

	
// When deleting a department, we need to null all the references 
// to that department. Therefore, the "toEmployee" relationship is
// used to find the employee records attached to a given department.
// All department ids will then be nulled out.
  

- (BOOL)controller:controller willDeleteObject:object
{
    
    NSArray *employeeArray = [object toEmployee];
	NSEnumerator *enumerator = [employeeArray objectEnumerator];
	EOGenericRecord	*employee;
	
	while((employee = [enumerator nextObject]) != nil) {
		[employee setObject:[EONull null] forKey:@"DeptId"];
		[(id)[controller dataSource] updateObject:employee];
	}

    return YES;
}


// Implementing this delegate method allows the controller to discard
// pending edits. In this example, pending edits are edits rejected by
// the validation mechanism, hence they can be discarded.
 
- (BOOL) controllerWillDiscardEdits:(EOController *)controller
{
	return YES;
}
	
@end

@implementation Text(printResults)

- appendToText:(const char *)newText
{
	int currentLength = [self textLength];

	[self setSel:currentLength :currentLength];
	[self replaceSel:newText];
	[self scrollSelToVisible];
	return self;
}

@end
